﻿using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Text;

namespace DimensionsHelper.LanguageService.Syntax;

/// <summary>
///     词法扫描生成的简单Token
/// </summary>
[DebuggerDisplay("{ToDisplayString(),nq}")]
public class SourceNode : AbstractTextNode
{
    internal SourceNode(SyntaxKinds syntaxKind) : base(syntaxKind, null)
    {
    }


    internal SourceNode(SourceNode? parent, SyntaxKinds syntaxKind) : base(syntaxKind, parent)
    {
    }


    public static SourceNode Default { get; } = new(SyntaxKinds.Unknown);


    /// <summary>
    ///     节点的数字数值，只有在节点是数字是才有效
    /// </summary>
    public double TokenNumberValue { get; private set; }


    /// <summary>
    ///     节点的布尔类型值，只有在节点是布尔常量时才有效
    /// </summary>
    public bool TokenBooleanValue { get; private set; }


    /// <summary>
    ///     如果节点前是换行符，返回<see langword="true" />
    /// </summary>
    public bool HasPrecedingLinebreak => GetFlags(SyntaxNodeFlags.PrecedingLinebreak);


    /// <summary>
    ///     如果节点后是换行符，返回<see langword="true" />
    /// </summary>
    public bool HasTrailingLinebreak => GetFlags(SyntaxNodeFlags.TrailingLinebreak);


    /// <summary>
    ///     此节点是否在宏扩展节点中并且通过宏展开和其他节点合并。
    ///     如果此值是true，在 Parser 中经过此节点时应当跳过。
    /// </summary>
    public bool IsMacroExpansionSkipped { get; internal set; }


    /// <summary>
    ///     如果此节点已经被宏符号引用，将此属性设置为<see langword="true" />
    /// </summary>
    public bool IsMacroSymbolReferenced { get; internal set; }


    public override bool IsEmpty => ReferenceEquals(this, Default) || Width == 0;


    /// <summary>
    ///     如果此节点在宏定义列表中，设置此属性为<see langword="true" />
    /// </summary>
    public bool IsMacroDefinition { get; internal set; }


    /// <summary>
    ///     如果此节点在Parser中需要跳过，设置此属性为<see langword="true" />
    /// </summary>
    public virtual bool IsParserSkipped { get; internal set; }


    /// <summary>
    ///     如果节点是宏扩展节点，返回<see langword="true" />
    /// </summary>
    public bool IsMacroExpansion => IsNode(SyntaxKinds.MacroExpansion);


    /// <summary>
    ///     如果节点是注释指令，返回<see langword="true" />
    /// </summary>
    public bool IsDirective => IsNode(SyntaxKinds.Directive);


    /// <summary>
    ///     如果节点是预处理一元操作符，返回<see langword="true" />
    /// </summary>
    public bool IsPreUnaryOperator =>
        SyntaxFacts.IsUnaryOperator(Kind, OperatorContexts.Preprocessor);


    /// <summary>
    ///     如果节点是预处理二元操作符，返回<see langword="true" />
    /// </summary>
    public bool IsPreBinaryOperator =>
        SyntaxFacts.IsBinaryOperator(Kind, OperatorContexts.Preprocessor);


    /// <summary>
    ///     如果节点是脚本的一元表达式，返回<see langword="true" />
    /// </summary>
    public bool IsScriptUnaryOperator =>
        SyntaxFacts.IsUnaryOperator(Kind, OperatorContexts.Script);


    /// <summary>
    ///     如果节点是脚本的二元表达式，返回<see langword="true" />
    /// </summary>
    public bool IsScriptBinaryOperator =>
        SyntaxFacts.IsBinaryOperator(Kind, OperatorContexts.Script);


    /// <summary>
    ///     如果节点是空字符串，返回<see langword="true" />
    /// </summary>
    public bool IsEmptyString => IsString && string.IsNullOrEmpty(Text);


    /// <summary>
    ///     如果节点是标识符，返回<see langword="true" />
    /// </summary>
    public bool IsIdentifier => IsNode(SyntaxKinds.Identifier);


    /// <summary>
    ///     如果节点是字符串，返回<see langword="true" />
    /// </summary>
    public bool IsString => IsNode(SyntaxKinds.String);


    /// <summary>
    ///     如果节点是数字，返回<see langword="true" />
    /// </summary>
    public bool IsNumber => IsOneOfNode(SyntaxKinds.Integer, SyntaxKinds.Double);


    /// <summary>
    ///     如果节点是Token节点，返回<see langword="true" />
    /// </summary>
    public bool IsTokenNode => SyntaxFacts.IsToken(Kind);


    /// <summary>
    ///     如果节点是<see cref="SyntaxKinds.Eof" />，返回<see langword="true" />
    /// </summary>
    public bool IsEof => IsNode(SyntaxKinds.Eof);


    /// <summary>
    ///     如果节点是关键字，返回<see langword="true" />。
    /// </summary>
    public bool IsKeywordToken(KeywordContexts contexts = KeywordContexts.All)
    {
        return SyntaxFacts.IsKeyword(Kind, contexts);
    }


    public static bool IsNullOrDefault([NotNullWhen(false)] SourceNode? node)
    {
        return node == null || ReferenceEquals(node, Default) || node.IsEmpty;
    }

    internal SourceNode UpdateValue(bool booleanValue)
    {
        TokenBooleanValue = booleanValue;
        return this;
    }


    internal SourceNode UpdateValue(double numberValue)
    {
        TokenNumberValue = numberValue;
        return this;
    }


    internal SourceNode UpdateValue(int numberValue)
    {
        TokenNumberValue = numberValue;
        return this;
    }


    public virtual string ToDisplayString()
    {
        return $"{Span} - {Text}";
    }


    public override void AcceptVisitor<T>(SyntaxNodeVisitor<T> visitor)
    {
        visitor.VisitComment(this);
    }
}


public static class SourceNodeExtensions
{
    private const string MacroVaArgs = "__VA_ARGS__";
    private const string MacroFile = "__FILE__";
    private const string MacroDate = "__DATE__";
    private const string MacroTime = "__TIME__";
    private const string MacroLine = "__LINE__";


    /// <summary>
    ///     如果标识符是特殊预处理参数 __VA_ARGS__，返回<see langword="true" />
    /// </summary>
    public static bool IsMacroVaArgs(this ITextRange range)
    {
        return range.Text == MacroVaArgs;
    }

    /// <summary>
    ///     如果标识符是特殊预处理参数 __FILE__，返回<see langword="true" />
    /// </summary>
    public static bool IsMacroFile(this ITextRange range)
    {
        return range.Text == MacroFile;
    }


    /// <summary>
    ///     如果标识符是特殊预处理参数 __DATE__，返回<see langword="true" />
    /// </summary>
    public static bool IsMacroDate(this ITextRange range)
    {
        return range.Text == MacroDate;
    }


    /// <summary>
    ///     如果标识符是特殊预处理参数 __TIME__，返回<see langword="true" />
    /// </summary>
    public static bool IsMacroTime(this ITextRange range)
    {
        return range.Text == MacroTime;
    }


    /// <summary>
    ///     如果标识符是特殊预处理参数 __LINE__，返回<see langword="true" />
    /// </summary>
    public static bool IsMacroLine(this ITextRange range)
    {
        return range.Text == MacroLine;
    }


    /// <summary>
    ///     如果标识符是预处理预定义的名称，返回<see langword="true" />
    /// </summary>
    public static bool IsMacroPreDefined(this ITextRange range)
    {
        return IsMacroVaArgs(range) || IsMacroFile(range) || IsMacroDate(range) || IsMacroTime(range) ||
               IsMacroLine(range);
    }
}


public static partial class NodeFactory
{
    public static SourceNode CreateSourceToken(SyntaxKinds token)
    {
        Debug.Assert(SyntaxFacts.IsToken(token) || SyntaxFacts.IsKeyword(token));
        return new SourceNode(token);
    }
}


public static partial class NodeConverter
{
    /// <summary>
    ///     将宏参数中的节点列表合并并转换成字符串，每个节点之间最多带有一个空格。
    /// </summary>
    /// <param name="parent">合并后节点的父节点，可以是<see langword="null" /></param>
    /// <param name="nodes">宏参数的节点列表</param>
    /// <returns>合并后的字符串节点</returns>
    public static SourceString CombineAsString(SourceNode? parent, MacroArgument nodes)
    {
        Debug.Assert(nodes != null);
        SourceString strNode = NodeFactory.CreateSourceString(parent);
        if (!nodes.Any())
        {
            strNode.Update(nodes.Span);
        }
        else
        {
            StringBuilder strValue = new();
            bool isFirst = true;
            int lastEnd = 0;
            foreach (SourceNode node in nodes)
            {
                if (isFirst)
                {
                    lastEnd = node.End;
                    isFirst = false;
                }
                else
                {
                    if (node.Start - lastEnd >= 1)
                    {
                        strValue.Append(' ');
                    }

                    lastEnd = node.End;
                }

                strValue.Append(node.Text);
            }

            strNode.Update(nodes.Span, strValue.ToString());
        }

        return strNode;
    }


    /// <summary>
    ///     合并文本节点为一个标识符。
    /// </summary>
    /// <param name="nodes">文本节点列表</param>
    /// <param name="text">
    ///     替换的节点内容，如果此参数是<see langword="null" />，
    ///     将依据节点文本确定合并后的内容。
    /// </param>
    /// <returns>合并后的标识符</returns>
    public static SourceIdentifier CombineAsIdentifier(ITextNode[] nodes, string? text = null)
    {
        Debug.Assert(nodes.Length > 0);
        int start = nodes[0].Span.Start;
        int end = nodes[^1].Span.End;

        if (string.IsNullOrEmpty(text))
        {
            StringBuilder builder = new();
            foreach (ITextNode n in nodes)
            {
                builder.Append(n.Text);
            }

            text = builder.ToString();
        }

        SourceIdentifier id = NodeFactory.CreateSourceIdentifier(null, text);
        id.Update(start, end);
        return id;
    }
}